package ace.tool.generate.code.impl.manager.core;

import ace.tool.generate.code.impl.generator.DefaultGenerator;
import ace.tool.generate.code.impl.manager.common.EntityModelManager;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.GenerateContext;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.ApiClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.DtoClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.EntityClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.RepositoryClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.RestAdminControllerClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.test.TestApiTestClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.test.TestBaseTestClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.test.TestBoostrapMainApplicationClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.test.TestGraphqlAdminControllerTestClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.test.TestGraphqlTestFileBundle;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.test.TestMainSuiteClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.test.TestResourceApplicationYmlFileInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.test.TestRestAdminControllerTestClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.test.TestStartupTestClassInfo;
import java.util.HashMap;
import java.util.Map;
import lombok.SneakyThrows;

/**
 * @author caspar
 * @date 2024/1/11 10:12 core project manager generator
 */
public class CoreProjectTestManager {

  private final GenerateContext context;
  private final EntityModelManager entityModelManager = new EntityModelManager();

  public CoreProjectTestManager(GenerateContext context) {
    this.context = context;
  }

  @SneakyThrows
  public void generate() {

    this.generateMainSuiteTest();

    this.generateMainBoostrapApplicationTest();

    this.generateTestResourceApplicationYml();

    this.generateTestResourceGraphqlTestFiles();

    this.generateBaseTestClass();

    this.generateStartupTestClass();

    this.generateApiTestClasses();

    this.generateGraphqlAdminControllerTestClasses();

    this.generateRestAdminControllerTestClasses();
  }

  private void generateRestAdminControllerTestClasses() {
    context.getModuleProject().getCoreProject().getTestRestAdminControllerTestClassInfos().stream()
        .forEach(this::generateRestAdminControllerTestClass);
  }

  private void generateRestAdminControllerTestClass(
      TestRestAdminControllerTestClassInfo testRestAdminControllerTestClassInfo) {
    EntityClassInfo entityClassInfo =
        entityModelManager.getEntityClassInfoByEntityModelId(
            context, testRestAdminControllerTestClassInfo.getEntityModelId());
    TestBaseTestClassInfo baseTestClassInfo =
        context.getModuleProject().getCoreProject().getTestBaseTestClassInfo();
    RepositoryClassInfo repositoryClassInfo =
        entityModelManager.getRepositoryByEntityModelId(
            context.getModuleProject().getCoreProject().getRepositoryClassInfos(),
            testRestAdminControllerTestClassInfo.getEntityModelId());
    RestAdminControllerClassInfo restAdminControllerClassInfo =
        entityModelManager.getRestAdminControllerClassInfoByEntityModelId(
            context, entityClassInfo.getEntityModelId());
    Map<String, Object> model = new HashMap<>();
    model.put(
        "packageFullName",
        testRestAdminControllerTestClassInfo.getInfo().getClassPackageFullName());
    model.put("baseTestClassFullName", baseTestClassInfo.getInfo().getClassFullName());
    model.put("entityClassFullName", entityClassInfo.getInfo().getClassFullName());
    model.put("entityClassName", entityClassInfo.getInfo().getClassName());
    model.put("entityRepositoryClassFullName", repositoryClassInfo.getInfo().getClassFullName());
    model.put(
        "restAdminControllerClassFullName",
        restAdminControllerClassInfo.getInfo().getClassFullName());
    model.put("entityRemark", entityClassInfo.getModel().getRemark());
    model.put("className", testRestAdminControllerTestClassInfo.getInfo().getClassName());
    model.put("entityRepositoryClassName", repositoryClassInfo.getInfo().getClassName());
    model.put("entityIdType", entityClassInfo.getModel().getEntityIdType());
    model.put("securityPermissionPrefix", testRestAdminControllerTestClassInfo.getSecurityPermissionPrefix());
    model.put("source", testRestAdminControllerTestClassInfo);
    model.put("restAdminControllerClassName", restAdminControllerClassInfo.getInfo().getClassName());

    String templateFile = "/schema/core-project/test/java/test/test/controller/rest/admin/CoreProjectTestAdminRestControllerTestClass.ftl";
    String outputFileFullName =
        testRestAdminControllerTestClassInfo.getInfo().getClassFileFullName();
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generateGraphqlAdminControllerTestClasses() {
    context
        .getModuleProject()
        .getCoreProject()
        .getTestGraphqlAdminControllerTestClassInfos()
        .stream()
        .forEach(this::generateGraphqlAdminControllerTestClass);
  }

  private void generateGraphqlAdminControllerTestClass(
      TestGraphqlAdminControllerTestClassInfo testGraphqlAdminControllerTestClassInfo) {
    EntityClassInfo entityClassInfo =
        entityModelManager.getEntityClassInfoByEntityModelId(
            context, testGraphqlAdminControllerTestClassInfo.getEntityModelId());
    TestBaseTestClassInfo baseTestClassInfo =
        context.getModuleProject().getCoreProject().getTestBaseTestClassInfo();
    RepositoryClassInfo repositoryClassInfo =
        entityModelManager.getRepositoryByEntityModelId(
            context.getModuleProject().getCoreProject().getRepositoryClassInfos(),
            testGraphqlAdminControllerTestClassInfo.getEntityModelId());
    TestGraphqlTestFileBundle testGraphqlTestFileBundle =
        entityModelManager.getTestGraphqlTestFileBundleByEntityModelId(
            context, entityClassInfo.getEntityModelId());
    Map<String, Object> model = new HashMap<>();
    model.put(
        "packageFullName",
        testGraphqlAdminControllerTestClassInfo.getInfo().getClassPackageFullName());
    model.put("baseTestClassFullName", baseTestClassInfo.getInfo().getClassFullName());
    model.put("entityClassFullName", entityClassInfo.getInfo().getClassFullName());
    model.put("entityClassName", entityClassInfo.getInfo().getClassName());
    model.put("entityRepositoryClassFullName", repositoryClassInfo.getInfo().getClassFullName());
    model.put("entityRemark", entityClassInfo.getModel().getRemark());
    model.put("className", testGraphqlAdminControllerTestClassInfo.getInfo().getClassName());
    model.put("entityRepositoryClassName", repositoryClassInfo.getInfo().getClassName());
    model.put(
        "graphqlMethodGet",
        testGraphqlTestFileBundle.getTestGraphqlTestGetFileInfo().getInfo().getDocumentName());
    model.put(
        "graphqlMethodList",
        testGraphqlTestFileBundle.getTestGraphqlTestListFileInfo().getInfo().getDocumentName());
    model.put(
        "graphqlMethodSearch",
        testGraphqlTestFileBundle.getTestGraphqlTestSearchFileInfo().getInfo().getDocumentName());
    model.put(
        "graphqlMethodAdd",
        testGraphqlTestFileBundle.getTestGraphqlTestAddFileInfo().getInfo().getDocumentName());
    model.put(
        "graphqlMethodSave",
        testGraphqlTestFileBundle.getTestGraphqlTestSaveFileInfo().getInfo().getDocumentName());
    model.put(
        "graphqlMethodModify",
        testGraphqlTestFileBundle.getTestGraphqlTestModifyFileInfo().getInfo().getDocumentName());
    model.put(
        "graphqlMethodRemove",
        testGraphqlTestFileBundle.getTestGraphqlTestRemoveFileInfo().getInfo().getDocumentName());
    model.put(
        "graphqlMethodRemoveLogic",
        testGraphqlTestFileBundle
            .getTestGraphqlTestRemoveLogicFileInfo()
            .getInfo()
            .getDocumentName());
    model.put("entityIdType", entityClassInfo.getModel().getEntityIdType());
    model.put(
        "securityPermissionPrefix",
        testGraphqlAdminControllerTestClassInfo.getSecurityPermissionPrefix());

    String templateFile = "/schema/core-project/test/java/test/test/controller/graphql/admin/CoreProjectTestAdminGraphqlControllerTestClass.ftl";
    String outputFileFullName =
        testGraphqlAdminControllerTestClassInfo.getInfo().getClassFileFullName();
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generateTestResourceGraphqlTestFiles() {
    new CoreProjectTestResourceGraphqlTestFilesManager(context).generate();
  }

  private void generateStartupTestClass() {
    TestStartupTestClassInfo testStartupTestClassInfo =
        context.getModuleProject().getCoreProject().getTestStartupTestClassInfo();
    TestBaseTestClassInfo testBaseTestClassInfo =
        context.getModuleProject().getCoreProject().getTestBaseTestClassInfo();
    Map<String, Object> model = new HashMap<>();
    model.put("packageFullName", testStartupTestClassInfo.getInfo().getClassPackageFullName());
    model.put("className", testStartupTestClassInfo.getInfo().getClassName());
    model.put("baseTestClassName", testBaseTestClassInfo.getInfo().getClassName());
    model.put("baseTestClassFullName", testBaseTestClassInfo.getInfo().getClassFullName());

    String templateFile = "/schema/core-project/test/java/test/test/CoreProjectTestStartupTestClass.ftl";
    String outputFileFullName = testStartupTestClassInfo.getInfo().getClassFileFullName();
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generateApiTestClasses() {
    context.getModuleProject().getCoreProject().getTestApiTestClassInfos().stream()
        .forEach(this::getTestApiTestClassInfo);
  }

  private void getTestApiTestClassInfo(TestApiTestClassInfo testApiTestClassInfo) {
    EntityClassInfo entityClassInfo =
        entityModelManager.getEntityClassInfoByEntityModelId(
            context, testApiTestClassInfo.getEntityModelId());
    DtoClassInfo dtoClassInfo =
        entityModelManager.getDtoClassInfoByEntityModelId(
            context, testApiTestClassInfo.getEntityModelId());
    ApiClassInfo apiClassInfo =
        entityModelManager.getApiClassInfoByEntityModelId(
            context, testApiTestClassInfo.getEntityModelId());
    TestBaseTestClassInfo baseTestClassInfo =
        context.getModuleProject().getCoreProject().getTestBaseTestClassInfo();
    Map<String, Object> model = new HashMap<>();
    model.put("packageFullName", testApiTestClassInfo.getInfo().getClassPackageFullName());
    model.put("apiClassFullName", apiClassInfo.getInfo().getClassFullName());
    model.put("dtoClassFullName", dtoClassInfo.getInfo().getClassFullName());
    model.put("baseTestClassFullName", baseTestClassInfo.getInfo().getClassFullName());

    model.put("entityRemark", entityClassInfo.getModel().getRemark());
    model.put("className", testApiTestClassInfo.getInfo().getClassName());
    model.put("apiClassName", apiClassInfo.getInfo().getClassName());
    model.put("dtoClassName", dtoClassInfo.getInfo().getClassName());
    model.put("entityIdType", entityClassInfo.getModel().getEntityIdType());

    String templateFile = "/schema/core-project/test/java/test/test/api/CoreProjectTestApiTestClass.ftl";
    String outputFileFullName = testApiTestClassInfo.getInfo().getClassFileFullName();
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generateBaseTestClass() {
    TestBaseTestClassInfo testBaseTestClassInfo =
        context.getModuleProject().getCoreProject().getTestBaseTestClassInfo();
    TestBoostrapMainApplicationClassInfo mainApplicationClassInfo =
        context.getModuleProject().getCoreProject().getTestBoostrapMainApplicationClassInfo();
    Map<String, Object> model = new HashMap<>();
    model.put("packageFullName", testBaseTestClassInfo.getInfo().getClassPackageFullName());
    model.put(
        "mainApplicationTestClassFullName", mainApplicationClassInfo.getInfo().getClassFullName());
    model.put("mainApplicationTestClassName", mainApplicationClassInfo.getInfo().getClassName());

    String templateFile = "/schema/core-project/test/java/test/CoreProjectTestBaseTestClass.ftl";
    String outputFileFullName = testBaseTestClassInfo.getInfo().getClassFileFullName();
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generateTestResourceApplicationYml() {
    TestResourceApplicationYmlFileInfo testResourceApplicationYmlFileInfo =
        context.getModuleProject().getCoreProject().getTestResourceApplicationYmlFileInfo();

    Map<String, Object> model = new HashMap<>();
    model.put(
        "projectName", context.getModuleProject().getCoreProject().getInfo().getMavenArtifactId());

    String templateFile = "/schema/core-project/test/resources/CoreProjectTestResourceApplicationYmlFileInfo.ftl";
    String outputFileFullName = testResourceApplicationYmlFileInfo.getFileFullName();
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generateMainBoostrapApplicationTest() {
    TestBoostrapMainApplicationClassInfo applicationClassInfo =
        context.getModuleProject().getCoreProject().getTestBoostrapMainApplicationClassInfo();

    Map<String, Object> model = new HashMap<>();
    model.put("packageFullName", applicationClassInfo.getInfo().getClassPackageFullName());
    model.put("className", applicationClassInfo.getInfo().getClassName());

    String templateFile = "/schema/core-project/test/java/test/core/CoreProjectTestBoostrapApplicationTestClass.ftl";
    String outputFileFullName = applicationClassInfo.getInfo().getClassFileFullName();
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generateMainSuiteTest() {
    TestMainSuiteClassInfo testMainSuiteClassInfo =
        context.getModuleProject().getCoreProject().getTestMainSuiteClassInfo();

    Map<String, Object> model = new HashMap<>();
    model.put("packageFullName", testMainSuiteClassInfo.getInfo().getClassPackageFullName());
    model.put("className", testMainSuiteClassInfo.getInfo().getClassName());

    String templateFile = "/schema/core-project/test/java/test/CoreProjectTestMainSuiteTestClass.ftl";
    String outputFileFullName = testMainSuiteClassInfo.getInfo().getClassFileFullName();
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }
}
